<?php
// +----------------------------------------------------------------------
// | snake
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2022 http://baiyf.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: NickBai <1902822973@qq.com>
// +----------------------------------------------------------------------
namespace app\common\logic;

use think\Db;
use think\facade\Log;
use think\facade\Request;

class MemberLogic extends BaseLogic
{
    public function __construct($tableName = 'Member'){
        parent::__construct($tableName);
    }

    /**
     * 检测 是否 在 允许登录的范围
     * @param $type
     * @param $ip
     * @return float|string 大于 0  说明有禁用的
     */
    public function _checkMemberLoginByTypeAndIp($type,$ip){
        $where[] = ['type','=',$type];
        $where[] = ['ip','=',$ip];
        $where[] = ['is_break','=',1];
        $where[] = ['create_time','>',time()];
        // 大于 0  说明有禁用的
        return Db::name('MemberLog')->where($where)->count();
    }

    /**
     * 检测 1分钟内 是否需要 写入拦截数据
     * @param $type
     * @param $ip
     */
    public function _checkWriteIsBreak($type,$ip){
        /**
         * 允许错误次数
         */
        $errNum = 3;
        /**
         * 分钟 内
         */
        $jiange = 5;

        $where[] = ['type','=',$type];
        $where[] = ['ip','=',$ip];
        $where[] = ['create_time','>',(time() - (($jiange * 60) + 1))];
        //  1 分钟内 包含超过 3 条错误登录信息，则写入该IP禁用登录的数据
        if(Db::name('MemberLog')->where($where)->count() > $errNum){
            $this->_saveMemberLog([
                'type' => $type,
                'ip' => $ip,
                'is_break' => 1,
                'create_time' => (time() + (15 * 60)), // 插入禁止的分钟，默认禁止登录15分钟
                'remark' => '短时间内错误，禁止该IP登录15分钟'
            ]);
        }
    }

    public function _getMemberByWhere($where){
        $where[] = ['delete_time','<',1];
        return Db::name('Member')->where($where)->find();
    }

    /**
     * 更新会员信息
     * @param $memberId
     * @param $updateData
     * @return int|string
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function _updateMemberByMemberId($memberId,$updateData){
        $where[] = ['id','=',$memberId];
        return $this->_updateMember($where, $updateData);
    }

    /**
     * 更新会员信息
     * @param $where
     * @param $updateData
     * @return int|string
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function _updateMember($where,$updateData){
        return Db::name('Member')->where($where)->update($updateData);
    }

    /**
     * 变更会员手机号码，会先去掉已有的
     * @param $oldMemberId
     * @param $mobileModelId
     * @param $newMobile
     * @return array
     */
    public function changeMemberMobile($oldMemberId,$mobileModelId,$newMobile){
        // 启动事务
        Db::startTrans();
        try {
            // 先取消掉原绑定的手机号码
            $jieguo = $this->_updateMemberByMemberId($mobileModelId,['mobile'=>'']);
            if(false === $jieguo){
                exception('取消掉原绑定的手机号码 失败 $mobileModelId ' . $mobileModelId);
            }
            // 写入新的手机号码到当前登录账号用户
            $jieguo = $this->_updateMemberByMemberId($oldMemberId,['mobile'=>$newMobile]);

            if(false === $jieguo){
                exception('写入新的手机号码到当前登录账号用户 失败 $oldMemberId ' . $oldMemberId . ' $newMobile ' . $newMobile);
            }

            // 提交事务
            Db::commit();

            return $this->retSuccess('绑定成功', [
            ]);
        } catch (\Exception $e) {
            // 统一处理错误日志
            Log::error($e->getMessage());
            // 回滚事务
            Db::rollback();

            return $this->retError('绑定失败');
        }
    }

    /**
     * 根据 会员 ID 获取会员信息
     * @param $memberId
     * @return array|null|\PDOStatement|string|\think\Model
     */
    public function _getMemberByMemberId($memberId){
        $where[] = ['id','=',$memberId];

        return $this->_getMemberByWhere($where);
    }
    /**
     * 根据 openid 获取用户
     * @param $openId
     * @return array|null|\PDOStatement|string|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function _getMemberByOpenId($openId){
        $where[] = ['openid','=',$openId];

        return $this->_getMemberByWhere($where);
    }

    /**
     * 根据 微信信息 获取 会员ID，如果存在则直接返回会员ID
     * @param array $wechatInfo 微信信息
     * @param bool $isUpdate 是否更新
     * @return int|mixed|string
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws \think\exception\PDOException
     */
    public function _getMemberIdByWechat(array $wechatInfo,$isUpdate = false){
        if (!empty($wechatInfo['nickname'])) {
            // 过滤emoji，防止存储报错
            $wechatInfo['nickname'] = filterEmoji( $wechatInfo['nickname'] );
        }
        $user = $this->_getMemberByOpenId($wechatInfo['openid']);
        if (empty($user)) {
            return Db::name('Member')->insertGetId([
                'openid' => $wechatInfo['openid'],
                'nickname' => $wechatInfo['nickname'],
                'gender' => $wechatInfo['sex'],
                'avatar' => headimgurl2avatar($wechatInfo['headimgurl']),
                'language' => $wechatInfo['language'],
                'country' => $wechatInfo['country'],
                'province' => $wechatInfo['province'],
                'city' => $wechatInfo['city'],
                'is_follow' => $wechatInfo['is_follow'],
                'followtime' => $wechatInfo['followtime'],
                'create_time' => time(),
            ]);
        }
        else{
            // 更新
            if (true === $isUpdate) {
                Db::name('Member')->where('id',$user['id'])->update([
                    'nickname' => $wechatInfo['nickname'],
                    'gender' => $wechatInfo['sex'],
                    'avatar' => headimgurl2avatar($wechatInfo['headimgurl']),
                    'language' => $wechatInfo['language'],
                    'country' => $wechatInfo['country'],
                    'province' => $wechatInfo['province'],
                    'city' => $wechatInfo['city'],
                    'is_follow' => $wechatInfo['is_follow'],
                    'followtime' => $wechatInfo['followtime'],
                    'update_time' => time(),
                ]);
            }
            return $user['id'];
        }
    }

    /**
     * 写用户日志
     * @param $row
     */
    public function _saveMemberLog($row){
        if(empty($row['member_id'])){
            $row['member_id'] = 0;
        }
        if(empty($row['type'])){
            $row['type'] = 1;
        }
        if(empty($row['param'])){
            $row['param'] = '';
        }
        if(empty($row['return'])){
            $row['return'] = '';
        }
        if(empty($row['result'])){
            $row['result'] = 0;
        }
        if(empty($row['is_break'])){
            $row['is_break'] = 0;
        }
        if(empty($row['create_time'])){
            $row['create_time'] = time();
        }
        if(empty($row['remark'])){
            $row['remark'] = '';
        }
        if(is_array($row['param'])){
            $row['param'] = json_encode($row['param'], 256);
        }
        if(is_array($row['return'])){
            $row['return'] = json_encode($row['return'], 256);
        }
        return Db::name('MemberLog')->insertGetId([
            'member_id' => $row['member_id'],
            'type' => $row['type'],
            'ip' => $row['ip'],
            'param' => $row['param'],
            'return' => $row['return'],
            'result' => $row['result'],
            'is_break' => $row['is_break'],
            'create_time' => $row['create_time'],
            'remark' => $row['remark'],
        ]);
    }

    /**
     * 写用户登录日志
     * @param $row
     * @return int|string
     */
    public function _saveLoginMemberLog($row){
        $row['member_id'] = 0;
        $row['type'] = 1;
        if($this->_saveMemberLog($row)){
            // 写入成功
            $this->_checkWriteIsBreak($row['type'],$row['ip']);
        }
    }

    /**
     * 使用 手机号 密码 进行登录
     * @param $mobile
     * @param $pwd
     * @return array
     */
    public function loginByMobileAndPwd($mobile,$pwd){
        if(empty($mobile) || empty($pwd)){
            return $this->retError('手机号和密码不能为空');
        }

        $ip = Request::ip();

        if($this->_checkMemberLoginByTypeAndIp(1,$ip)){
            return $this->retError('登录错误超过3次，请15分钟后重试');
        }

        $where[] = ['mobile','=',$mobile];
        $userModel = $this->_getMemberByWhere($where);
        $logModel = [
            'ip' => $ip,
            'param' => [
                'mobile' => $mobile,
                'pwd' => $pwd,
            ],
            'return' => '',
            'result' => 1,
            'remark' => '',
        ];
        if (empty($userModel)) {
            // 写用户日志
            $jieguo = $this->retError('用户不存在');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '用户不存在';
            $this->_saveLoginMemberLog($logModel);
            return $jieguo;
        }
        if (encrypt_password($pwd,$userModel['salt']) !== $userModel['pwd']) {
            $jieguo = $this->retError('密码错误');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '密码错误';
            $this->_saveLoginMemberLog($logModel);
            return $jieguo;
        }
        // 黑名单判断
        if(!empty($userModel['is_black'])){
            $jieguo = $this->retError('黑名单用户，无法登录');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '黑名单用户，无法登录';
            $this->_saveLoginMemberLog($logModel);
            return $jieguo;
        }

        // 出掉敏感列
        unset($userModel['pwd']);
        unset($userModel['salt']);
        unset($userModel['status']);

        $jieguo = $this->retSuccess('登录成功',$userModel);
        $logModel['return'] = $jieguo;
        $logModel['remark'] = '登录成功';
        $logModel['result'] = 0; // 标记成功状态
        $this->_saveLoginMemberLog($logModel);

        return $jieguo;
    }

    public function regMember($params){
        if(empty($params['mobile'])){
            return $this->retError('手机号不能为空');
        }
        if(empty($params['vcode'])){
            return $this->retError('短信验证码不能为空');
        }
        if(empty($params['pwd'])){
            return $this->retError('密码不能为空');
        }
        if(empty($params['pwd2'])){
            return $this->retError('确认密码不能为空');
        }

        if(!isMobilePhone($params['mobile'])){
            return $this->retError('手机号不正确');
        }

        if($params['pwd'] !== $params['pwd2']){
            return $this->retError('密码和确认密码不一致');
        }

        // 验证 短信验证码
        $smsLogic = new SmsLogic(getProjectId());
        $jieguo = $smsLogic->checkVCode($params['mobile'],$params['vcode']);
        if($jieguo['err_code'] !== 0){
            return $jieguo;
        }
        // 短信验证码 验证通过

        // 检查手机号是否重复
        $where[] = ['mobile','=',$params['mobile']];
        if($this->_getCountByWhere($where,0,'Member') > 0){
            return $this->retError('手机号 已经存在');
        }

        $salt = random(8);
        $data = [
            'id' => 0,
            'mobile' => $params['mobile'],
            'pwd' => encrypt_password($params['pwd'],$salt),
            'salt' => $salt,
            'openid' => getWapMemberOpenId($params['mobile']),
            'nickname' => getWapMemberNickname($params['mobile']),
        ];
        $jieguo = $this->save($data,'Member');
        if($jieguo['err_code'] !== 0){
            $jieguo['msg'] = '注册失败';
            return $jieguo;
        }
        else{
            $jieguo['msg'] = '注册成功';
            return $jieguo;
        }
    }

    /**
     * 设置为默认地址
     * @param $maId
     * @param $memberId
     * @return bool
     */
    public function _updateAddressDefault($maId,$memberId)
    {
        // 先设置全部为不默认
        Db::name('MemberAddress')->where('member_id',$memberId)->setField('is_default',0);
        // 再设置选中的那个为默认
        Db::name('MemberAddress')->where('id',$maId)->setField('is_default',1);

        return true;
    }

    /**
     * 获取收货地址列表
     * @param int $memberId 会员ID
     * @param string $fields 要显示的列，默认返回全部
     * @return array|\PDOStatement|string|\think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function _getAddressList($memberId,$where = [],$fields = '*'){
        $where[] = ['member_id','=',$memberId];
        $where[] = ['delete_time','<',1];
        return Db::name('MemberAddress')->where($where)->order('is_default DESC,id DESC')->field($fields)->select();
    }

    /**
     * 删除会员地址
     * @param $where
     * @return int
     */
    public function _delAddress($where){
        $where[] = ['delete_time','<',1];
        return Db::name('MemberAddress')->where($where)->setField('delete_time',time());
    }

    /**
     * 按条件 获取会员地址
     * @param $where
     * @return array|null|\PDOStatement|string|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function _getAddressModelByWhere($where){
        $where[] = ['delete_time','<',1];
        return Db::name('MemberAddress')->where($where)->order('is_default DESC,id DESC')->find();
    }

    /**
     * 按 pk id 获取会员地址
     * @param $addressId
     * @return array|null|\PDOStatement|string|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function _getAddressModelByAddressId($addressId){
        $where[] = ['id','=',$addressId];
        return $this->_getAddressModelByWhere($where);
    }

    /**
     * 获取会员购物车
     * @param $memberId
     * @return array|\PDOStatement|string|\think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function _getMemberCartByMemberId($memberId){
        $where[] = ['mc.delete_time','<',1];
        $where[] = ['mc.member_id','=',$memberId];

        $fields[] = 'mc.id';
        $fields[] = 'mc.member_id';
        $fields[] = 'mc.goods_id';
        $fields[] = 'mc.total';
        $fields[] = 'mc.old_market_price';
        $fields[] = 'mc.goods_option_id';
        $fields[] = 'mc.is_selected';
        $fields[] = 'g.goods_name';
        $fields[] = 'g.thumbs';
        $fields[] = 'go.title';
        $fields[] = 'go.market_price';
        $fields[] = 'go.specs';
        $fields[] = 'go.stock';

        return Db::name('MemberCart')->alias('mc')->field(join(',',$fields))
            ->join('__GOODS__ g','g.id = mc.goods_id','LEFT')
            ->join('__GOODS_OPTION__ go','go.id = mc.goods_option_id','LEFT')
            ->where($where)
            ->select();
    }

    /**
     * 获取购物车里面指定会员商品总 条数
     * @param $memberId
     * @return float|string
     */
    public function _getMemberCartCountByMemberId($memberId){
        $where[] = ['mc.delete_time','<',1];
        $where[] = ['mc.member_id','=',$memberId];

        return Db::name('MemberCart')->alias('mc')
            ->where($where)
            ->count();
    }

    /**
     * 获取购物车里面指定会员商品总个数
     * @param $memberId
     * @return float
     */
    public function _getMemberCartTotalSumByMemberId($memberId){
        $where[] = ['mc.delete_time','<',1];
        $where[] = ['mc.member_id','=',$memberId];

        return Db::name('MemberCart')->alias('mc')
            ->where($where)
            ->sum('total');
    }

    /**
     * 添加到购物车
     * @param $memberId
     * @param $total
     * @param $goodsModel
     * @param int $isSelected
     * @param int $goodsOptionId 商品规则，目前未开启
     * @return int|string
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function _addGoodsToCart($memberId,$total,$goodsModel,$isSelected = 1,$goodsOptionId = 0){
        // 判断 member_id  goods_id  goods_option_id 是否存在 ，存在 总数加一
        $where[] = ['delete_time','<',1];
        $where[] = ['member_id','=',$memberId];
        $where[] = ['goods_id','=',$goodsModel['id']];
        $where[] = ['goods_option_id','=',$goodsOptionId];

        if (!is_numeric($total)) {
            $total = (int)$total;
        }

        $goodsOptionModel = getGoodsOption($goodsModel,$goodsOptionId);
        if(empty($goodsOptionModel)){
            return false;
        }

        $cartModel = Db::name('MemberCart')->where($where)->find();

        if(!empty($cartModel)){
            $jieguo = Db::name('MemberCart')->where([['id','=',$cartModel['id']]])->update([
                'market_price' => $goodsOptionModel['market_price'],
                'total' => Db::raw('total + ' . $total),
                'update_time' => time(),
            ]);
            if (false !== $jieguo) {
                return $cartModel['id'];
            }
            else{
                return 0;
            }
        }
        else{
            return Db::name('MemberCart')->insertGetId([
                'member_id' => $memberId,
                'goods_id' => $goodsModel['id'],
                'total' => $total,
                'market_price' => $goodsOptionModel['market_price'],
                'old_market_price' => $goodsOptionModel['market_price'],
                'goods_option_id' => $goodsOptionId,
                'is_selected' => $isSelected,
                'create_time' => time(),
            ]);
        }
    }

    /**
     * 删除购物车
     * @param $memberId
     * @param array $cartIds
     * @return int
     */
    public function _delCartByIds($memberId,array $cartIds){
        $where[] = ['delete_time','<',1];
        $where[] = ['member_id','=',$memberId];
        $where[] = ['id','IN',$cartIds];
        return Db::name('MemberCart')->where($where)->setField('delete_time', time());
    }

    /**
     * 更新购物车中的数量
     * @param $memberId
     * @param $cartId
     * @param $total
     * @return int
     */
    public function _updateCartTotalById($memberId,$cartId,$total){
        $where[] = ['delete_time','<',1];
        $where[] = ['member_id','=',$memberId];
        $where[] = ['id','=',$cartId];
        return Db::name('MemberCart')->where($where)->setField('total', $total);
    }

    /**
     * 减购物车里面的
     * @param $memberId
     * @param $cartId
     * @param $total
     * @return int|true
     * @throws \think\Exception
     */
    public function _minusCartTotalById($memberId,$cartId,$total){
        $where[] = ['delete_time','<',1];
        $where[] = ['member_id','=',$memberId];
        $where[] = ['id','=',$cartId];
        return Db::name('MemberCart')->where($where)->setDec('total', $total);
    }

    /**
     * 从购物车获取商品 包含商品信息 ，不含商品扩展信息
     * @param $memberId
     * @param $cartIds
     * @return array
     */
    public function _getCartGoodsIdsByCartIds($cartIds,$memberId = 0){
        $where[] = ['mc.delete_time','<',1];
        if (!empty($memberId)) {
            $where[] = ['mc.member_id','=',$memberId];
        }
        $where[] = ['mc.id','IN',$cartIds];

        $goods = model('Goods');
        $gFields = $goods->getTableFields();
        $noShowFields = ['id','create_time','update_time','delete_time'];
        $showFields = [];

        foreach ($gFields as $key => $field) {
            if(!in_array($field,$noShowFields)){
                $showFields[] = 'g.' . $field;
            }
        }
        return Db::name('MemberCart')->alias('mc')
            ->field('mc.*,' . join(',',$showFields))
            ->join('__GOODS__ g','g.id = mc.goods_id','LEFT')
            ->where($where)->select();
    }

    /**
     * 用手机号码修改密码
     * @param $mobile
     * @param $newPwd
     * @return array
     */
    public function changeMemberPwdByMobile($mobile,$newPwd){
        $where[] = ['mobile','=',$mobile];
        $memberModel = $this->_getMemberByWhere($where);

        $logModel = [
            'ip' => Request::ip(),
            'param' => [
                'method' => 'changeMemberPwdByMobile',
                'mobile' => $mobile,
                'newPwd' => $newPwd,
            ],
            'return' => '',
            'result' => 1,
            'remark' => '',
        ];
        if (empty($memberModel)) {
            // 写用户日志
            $jieguo = $this->retError('用户不存在');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '用户不存在';
            $this->_saveLoginMemberLog($logModel);
            return $jieguo;
        }

        $salt = random(8);
        $data = [
            'pwd' => encrypt_password($newPwd,$salt),
            'salt' => $salt,
        ];

        if (false !== Db::name('Member')->where($where)->setField($data)) {
            $jieguo = $this->retSuccess('修改密码成功');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '修改密码成功';
            $logModel['result'] = 0; // 标记成功状态
            $this->_saveLoginMemberLog($logModel);
        }
        else{
            $jieguo = $this->retError('修改密码失败');
            $logModel['return'] = $jieguo;
            $logModel['remark'] = '修改密码失败';
            $logModel['result'] = 1; // 标记成功状态
            $this->_saveLoginMemberLog($logModel);
        }

        return $jieguo;
    }
}